package com.alex.tube;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

public class TubeGen {

	private static final int MAX_VIDEOS = 100;

	private static final int LINE_LENGTH = 127;

	private FeedParser feedParser = new FeedParser();
	
	public TubeGen() {
		Properties props = new Properties();
		props.setProperty(RuntimeConstants.RESOURCE_LOADER, "classpath");
		props.setProperty("classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
		props.setProperty("org.apache.velocity.toolbox", "tools.xml");

		props.setProperty(RuntimeConstants.INPUT_ENCODING, "utf-8");
		props.setProperty(RuntimeConstants.OUTPUT_ENCODING, "utf-8");

		Velocity.init(props);
	}

	public void generate(InputStream feed, String template, File path) throws Exception {
		List<Category> categories = feedParser.parseFeed(feed);
		FileUtils.deleteDirectory(path);
		path.mkdirs();

		String indexPage = generageIndex(categories, template, path);
		FileUtils.writeStringToFile(new File(path, "index.html"), indexPage);

		for (Category category : categories) {
			int n = 0;
			List<String> videoURLs = new ArrayList<String>();
			for (Video video : category.getVideos()) {
				int weight = (int) Math.floor(Math.log((MAX_VIDEOS - n + 3) / 4) + 1);
				for (int i = 0; i < weight; i++) {
					videoURLs.add(align(video.getUrl()));	
				}
				if (++n == MAX_VIDEOS) {
					break;
				}
			}
			FileUtils.write(new File(path, category.getUrl() + ".list"), StringUtils.join(videoURLs, "\n"));
		}
		
		for (Category category : categories) {
			String categoryPage = generageCategory(category, template, path);
			FileUtils.writeStringToFile(new File(path, category.getUrl()), categoryPage);
		}

		writeTemplateFiles(template, path);
	}

	private String align(String url) {
		if (url.length() > LINE_LENGTH) {
			throw new RuntimeException();
		}
		int add = LINE_LENGTH - url.length();
		for (int i = 0; i < add; i++) {
			url += " ";
		}
		return url;
	}

	private void writeTemplateFiles(String template, File path) throws IOException {
		String[] classpath = System.getProperty("java.class.path").split(File.pathSeparator);
		for (String item : classpath) {
			File file = new File(item);
			if (file.isDirectory()) {
				File templatePath = getTemplatePath(template, file);
				if (templatePath == null) {
					continue;
				} else {
					FileUtils.copyDirectory(new File(templatePath, "files"), new File(path, "files"));
				}
			}
		}
	}

	private File getTemplatePath(String template, File dir) {
		if (!dir.isDirectory()) {
			return null;
		}
		for (File file : dir.listFiles()) {
			if (file.getName().equals(template)) {
				return file;
			} else {
				File res = getTemplatePath(template, file);
				if (res != null) {
					return res;
				}
			}
		}
		return null;
	}

	private String generageCategory(Category category, String templateName, File path) {
		VelocityContext context = new VelocityContext();
		context.put("template", templateName);
		context.put("category", category);
		context.put("maxVideos", MAX_VIDEOS);

		Template template = Velocity.getTemplate(templateName + "/category.vm");

		StringWriter writer = new StringWriter();

		template.process();
		template.merge(context, writer);

		return writer.toString();
	}

	private String generageIndex(List<Category> categories, String templateName, File path) {
		VelocityContext context = new VelocityContext();
		context.put("template", templateName);
		context.put("categories", categories);

		Template template = Velocity.getTemplate(templateName + "/index.vm");

		StringWriter writer = new StringWriter();

		template.process();
		template.merge(context, writer);

		return writer.toString();
	}

}
